package cn.genmer.test.security.tagMatch;

import cn.genmer.test.security.tagMatch.locationtagtokenizer.Address;
import cn.genmer.test.security.tagMatch.locationtagtokenizer.TrieNode;
import cn.genmer.test.security.utils.AddressUtils;
import cn.genmer.test.security.utils.RandomAddressGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;

@Component
@Slf4j
public class LocationTagTokenizer {
    private final ConcurrentMap<String, TrieNode> sourceTries = new ConcurrentHashMap<>();

//    @Autowired
//    LocationTagMapper locationTagMapper;

    /**
     * 初始化数据
     */
    public void initData() {
        long start = System.currentTimeMillis();

        List<String> randomAddressList = RandomAddressGenerator.generateRandomAddresses(100000);
        log.info("开始加载随机地址数据: {}ms", System.currentTimeMillis() - start);


        long start2 = System.currentTimeMillis();
        int i=0;
        for (String tag : randomAddressList) {
            i++;
            List<Address> addressList = convertLocationTagPOToAddressList("NO"+i, AddressUtils.filterIllegalChar(tag),"gis");
            for (Address address : addressList) {
                addAddressToTrie(address);
            }
        }
        long end = System.currentTimeMillis();
        long duration = end - start2;
        log.info("数据加载时间: {}ms", duration);

        long l2 = System.currentTimeMillis();
        List<Address> tokenize = tokenize("北京市天河区199号", "gis");

        log.info("匹配数据耗时: {}ms", System.currentTimeMillis() - l2);
        System.out.println();
    }


    public List<Address> convertLocationTagPOToAddressList(String id, String name, String source) {
        List<Address> addressList = new ArrayList<>();
        Address address = new Address(id, name, source);
        addressList.add(address);
        return addressList;
    }


    // 将 Address 对象添加到指定数据源的 Trie 中
    public Boolean addAddressToTrie(Address address) {
        // 校验 address 对象及其内部字段不为空
        if (address == null ||
                address.getId() == null ||
                address.getAddress() == null ||
                address.getSource() == null) {
            throw new IllegalArgumentException("address 参数不能为空");
        }

        String source = address.getSource();
        TrieNode node = sourceTries.computeIfAbsent(source, k -> new TrieNode());  // 原子操作

        for (char c : address.getAddress().toCharArray()) {
            node = node.children.computeIfAbsent(c, k -> new TrieNode());
        }

        node.addressMap.put(source, address);  // 在节点中存储 Address 对象
        node.isEndOfWord.set(true);

        return true;
    }


    // 分词方法，返回匹配的 Address 对象列表
    public List<Address> tokenize(String text, String source) {
        if (StringUtils.isBlank(text) || StringUtils.isBlank(source)) {
            throw new IllegalArgumentException("tokenize 参数不能为空");
        }

        List<Address> addresses = new ArrayList<>();
        TrieNode root = sourceTries.get(source);
        int start = 0;

        while (start < text.length()) {
            TrieNode node = root;
            int end = start;
            boolean found = false;

            while (end < text.length()) {
                char c = text.charAt(end);

                if (node == null) {
                    break;
                }

                node = node.children.get(c);

                if (node == null) {
                    break;
                }
                if (node.isEndOfWord.get() && node.addressMap.containsKey(source)) {
                    addresses.add(node.addressMap.get(source));  // 将匹配到的 Address 对象添加到结果列表中
                    found = true;
                }
                end++;
            }

            if (found) {
                start = end;
            } else {
                start++;
            }
        }

        return addresses;
    }

    // 删除地址的方法
    public boolean deleteAddress(String address, String source) {
        if (StringUtils.isBlank(address) || StringUtils.isBlank(source)) {
            throw new IllegalArgumentException("deleteAddress 参数不能为空");
        }

        return deleteWordsFromTrie(source, address, sourceTries.get(source));
    }


    // 在 deleteWordsFromTrie 方法中使用 ConcurrentLinkedDeque
    private boolean deleteWordsFromTrie(String source, String word, TrieNode root) {
        if (root == null) {
            return false;
        }

        ConcurrentLinkedDeque<TrieNode> stack = new ConcurrentLinkedDeque<>();
        ConcurrentLinkedDeque<Character> charStack = new ConcurrentLinkedDeque<>();
        ConcurrentLinkedDeque<TrieNode> parentStack = new ConcurrentLinkedDeque<>();
        TrieNode current = root;
        TrieNode parent = null;
        boolean isDeleted = false;

        for (char c : word.toCharArray()) {
            parent = current;
            parentStack.push(parent);
            charStack.push(c);

            if (current.children == null) {
                current.children = new ConcurrentHashMap<>();
            }

            TrieNode nextNode = current.children.get(c);
            if (nextNode == null) {
                return false;
            }
            current = nextNode;
            stack.push(current);
        }

        synchronized (current) {
            if (!current.isEndOfWord.get() || !current.addressMap.containsKey(source)) {
                return false;
            }
            current.isEndOfWord.set(false);
            current.addressMap.remove(source);
            isDeleted = true;
        }

        while (!stack.isEmpty()) {
            current = stack.poll();
            char c = charStack.poll();
            parent = parentStack.poll();

            if (parent == null) {
                continue;
            }

            if (parent.children == null) {
                parent.children = new ConcurrentHashMap<>();
            }

            boolean hasChildren = !current.children.isEmpty();
            boolean hasAddress = !current.addressMap.isEmpty();

            if (!hasChildren && !hasAddress && !current.isEndOfWord.get()) {
                parent.children.remove(c);
            } else {
                break;
            }
        }

        return isDeleted;
    }

    public static void main(String[] args) {

    }

}
